using System;
using Microsoft.SPOT;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.IO;

namespace Elk232toEthernet
{
    public delegate HttpWebResponse RequestReceivedDelegate(HttpWebRequest request);

    public class WebServer
    {
        private Socket newSocket = null;
        private Socket client = null;

        public WebServer()
        {
            newSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            newSocket.Bind(new IPEndPoint(IPAddress.Any, 80));
            newSocket.Listen(10);
        }

        ~WebServer()
        {
            if (client != null) client.Close();
            if (newSocket != null) newSocket.Close();
        }

        public void WaitForRequest(RequestReceivedDelegate requestReceived)
        {
            try
            {
                client = newSocket.Accept(); //IMPORTANT: This is a blocking call. It would sit here until data arrives.
                using (var incommingStream = new NetworkStream(client))
                {
                    while (true)
                    {
                        if (incommingStream.DataAvailable)
                        {
                            SendResponse(requestReceived(new HttpWebRequest(incommingStream)), incommingStream);
                            break;
                        }
                    }
                }
                client.Close();
                client = null;
            }
            catch (Exception e)
            {
                Debug.Print(e.ToString());
            }
        }

        public void SendResponse(HttpWebResponse response, Stream outgoingStream) // Sends responseStr then closes the connection.
        {
            try
            {
                outgoingStream.Write(response.Header, 0, response.Header.Length);

                if (response.ResponseType == HttpWebResponse.ResponseTypes.FILE)
                {
                    using (var fileStream = new FileStream(response.FileName, FileMode.Open))
                    {
                        CopyStream(fileStream, outgoingStream);
                    }
                }
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
                Debug.Print(e.StackTrace);
            }
        }

        public static void CopyStream(Stream source, Stream target) 
        { 
            byte[] buf = new byte[8000];
            int read = 0;
            while (true)
            {
                read = source.Read(buf, 0, buf.Length);
                if (read <= 0)
                    return;
                target.Write(buf, 0, read);
            }
        }
     }



    public class HttpWebRequest
    {
        public enum Method
        {
            GET,
            HEAD,
            POST,
            PUT,
            DELETE,
            TRACE,
            CONNECT,
            OPTIONS
        }
        
        #region Constructors
        public HttpWebRequest(Stream stream)
        {

            if (!stream.CanRead)
            {
                throw new InvalidOperationException("HttpWebRequest cannot parse an empty request string.");
            }
            this.Stream = stream;

            StreamReader reader = new StreamReader(stream);
            string header = reader.ReadLine();
            Debug.Print("Incomming Header: " + header);
            string[] tokens = header.Split(' ');

            if (tokens.Length <= 0)
            {
                //log exception here
            }

            switch (tokens[0])
            {
                case "GET":
                    RequestMethod = Method.GET;
                    break;
                case "POST":
                    RequestMethod = Method.POST;
                    break;
                case "HEAD":
                    RequestMethod = Method.HEAD;
                    break;
                case "PUT":
                    RequestMethod = Method.PUT;
                    break;
                case "DELETE":
                    RequestMethod = Method.DELETE;
                    break;
                case "TRACE":
                    RequestMethod = Method.TRACE;
                    break;
                case "CONNECT":
                    RequestMethod = Method.CONNECT;
                    break;
                case "OPTIONS":
                    RequestMethod = Method.OPTIONS;
                    break;
            }

            if (tokens[1][0] == '/')
            {
                Uri = tokens[1].Substring(1, tokens[1].Length - 1);
            }
            else
            {
                Uri = tokens[1];
            }
            
        }
        #endregion

        #region Public Properties
        public Method RequestMethod
        {
            get;
            set;
        }

        public string Uri
        {
            get;
            set;
        }

        public Stream Stream
        {
            get;
            set;
        }
        #endregion

        public void EndRequest()
        {
            //Read in the rest of the incommingStream to let the client complete sending data.
            byte[] buffer = new byte[128];
            if (((NetworkStream)Stream).DataAvailable)
            {
                while (Stream.Read(buffer, 0, buffer.Length) > 0)
                {
                    if (!((NetworkStream)Stream).DataAvailable) break;
                }
            }
        }
    }

    public class HttpWebResponse
    {
        public enum ResponseTypes
        {
            FILE,
            TEXT
        }

        #region Public Properties
        public byte[] Header
        {
            get;
            set;
        }

        public string FileName
        {
            get;
            set;
        }

        public ResponseTypes ResponseType
        {
            get;
            set;
        }
        #endregion
        
        #region Constructor
        public HttpWebResponse()
        {
            //this.Header = new byte[] { };
        }
        #endregion

        public void AddHeader(string header)
        {
            byte[] responseBytes = Encoding.UTF8.GetBytes(header);
            if (this.Header == null)
            {
                this.Header = responseBytes;
            }
            else
            {
                byte[] newHeader = new byte[Header.Length + responseBytes.Length];
                Header.CopyTo(newHeader, 0);
                responseBytes.CopyTo(newHeader, Header.Length);
                Header = newHeader;
            }
        }
    }
}
